package com.lncg.service.impl;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lncg.entity.CqzlDeviceReceiveData;
import com.lncg.mapper.CqzlDeviceReceiveDataMapper;
import com.lncg.service.ICqzlDeviceReceiveDataService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

/**
 * CQZL设备接收数据服务实现类
 * 实现CQZL设备数据的业务逻辑操作，包括JSON数据解析、UPSERT操作和历史记录管理。
 * 提供完整的CQZL协议数据处理流程，确保数据的一致性和可靠性。
 * @author LNCG
 * @version 1.0.0
 * @since 2024-10-17
 */
@Service
public class CqzlDeviceReceiveDataServiceImpl implements ICqzlDeviceReceiveDataService {
    
    /** 日志记录器 */
    private static final Logger logger = LoggerFactory.getLogger(CqzlDeviceReceiveDataServiceImpl.class);

    /** 表盘数 */
    private String dialNumber = "";
    /** CQZL设备数据数据访问对象 */
    @Autowired
    private CqzlDeviceReceiveDataMapper cqzlDeviceReceiveDataMapper;
    
    /**
     * 处理CQZL协议数据
     * 将CQZL协议数据转换为实体对象，执行UPSERT操作到主表，
     * 并插入历史记录到历史表。整个过程在事务中执行，确保数据一致性。
     * @param jsonData CQZL协议的JSON数据字符串
     * @param clientIpPort 客户端IP和端口信息
     * @return 处理结果信息
     */
    @Override
    @Transactional
    public String processCqzlData(String jsonData, String clientIpPort) {
        try {
            logger.info("开始处理CQZL协议数据（同步），客户端: {}", clientIpPort);
            
            // 1. 解析JSON数据为CQZL实体对象
            CqzlDeviceReceiveData deviceData = parseJsonToCqzlData(jsonData);
            if (deviceData == null) {
                return "CQZL协议数据解析失败";
            }
            
            // 设置IP端口信息
            deviceData.setIpport(clientIpPort);
            deviceData.setCreateBy("system");
            deviceData.setUpdateBy("system");

            // 2. 判断是否有pulse_convert_count属性，如果有就计算正累计的值，如果没有就直接使用原始的值
            if (deviceData.getPulseConvertCount() != null && deviceData.getPulseConvertCount() > 0) {
                // 如果有pulse_convert_count且值大于0，设置RTU类型为2
                deviceData.setRtuType((byte) 2);
                // 如果有pulse_convert_count且值大于0，计算正累计值
                try {
                    String calculatedZhengleiji = calculateZhengleijiFromCurrentData(deviceData);
                    deviceData.setZhengleiji(calculatedZhengleiji);
                    logger.info("成功计算正累计值，IMEI: {}，新的值: {}", deviceData.getImei(), calculatedZhengleiji);
                } catch (Exception e) {
                    logger.error("计算用水量失败，IMEI: {}，错误: {}，使用原始值", deviceData.getImei(), e.getMessage(), e);
                    // 计算失败时使用原始值
                }
            } else {
                // 如果没有pulse_convert_count或值为0，直接使用原始值
                logger.info("未找到有效的pulse_convert_count属性（值为null或0），使用原始的正累计值，IMEI: {}", deviceData.getImei());
                // 如果有pulse_convert_count且值大于0，设置RTU类型为1
                deviceData.setRtuType((byte) 1);
            }

            // 设置表盘数
            deviceData.setDialNumber(dialNumber);

            // 3. 执行UPSERT操作到主表（包含计算好的zhengleiji）
            int upsertResult = cqzlDeviceReceiveDataMapper.upsertByImei(deviceData);
            logger.info("成功UPSERT主表，IMEI: {}，影响行数: {}", deviceData.getImei(), upsertResult);
            
            // 4. 插入历史记录（包含计算好的zhengleiji）
            int historyResult = cqzlDeviceReceiveDataMapper.insertToHistory(deviceData);
            logger.info("成功插入历史表，IMEI: {}，影响行数: {}", deviceData.getImei(), historyResult);

            return "CQZL设备数据处理完成（同步），IMEI: " + deviceData.getImei();
            
        } catch (Exception e) {
            logger.error("处理CQZL协议数据失败，客户端: {}，错误: {}", clientIpPort, e.getMessage(), e);
            throw new RuntimeException("CQZL协议数据处理失败", e);
        }
    }
    
    /**
     * 将JSON数据解析为CQZL实体对象
     * 
     * @param jsonData JSON数据字符串
     * @return CQZL设备数据对象，解析失败时返回null
     */
    private CqzlDeviceReceiveData parseJsonToCqzlData(String jsonData) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            // 配置忽略未知字段，避免DI_COUNT1等大写字段名导致的反序列化错误
            mapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            
            JsonNode jsonNode = mapper.readTree(jsonData);
            
            // 1. 使用ObjectMapper进行基本字段映射
            CqzlDeviceReceiveData deviceData = mapper.treeToValue(jsonNode, CqzlDeviceReceiveData.class);
            
            // 2. 检查IMEI
            if (deviceData.getImei() == null || deviceData.getImei().isEmpty()) {
                logger.warn("CQZL协议数据缺少IMEI字段");
                return null;
            }
            
            // 3. 单独处理时间戳转换
            if (jsonNode.has("ts")) {
                try {
                    Long timestamp = jsonNode.get("ts").asLong();
                    Instant instant = Instant.ofEpochMilli(timestamp);
                    LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.of("Asia/Shanghai"));
                    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                    deviceData.setTs(localDateTime.format(formatter));
                    } catch (Exception e) {
                        logger.warn("时间戳转换失败: {}", e.getMessage());
                    }
            }
            
            // 4. 单独处理大小写不同的数字计数字段
            if (jsonNode.has("DI_COUNT1") && deviceData.getDiCount1() == null) {
                deviceData.setDiCount1(jsonNode.get("DI_COUNT1").asText());
            }
            if (jsonNode.has("DI_COUNT2") && deviceData.getDiCount2() == null) {
                deviceData.setDiCount2(jsonNode.get("DI_COUNT2").asText());
            }
            if (jsonNode.has("DI_COUNT3") && deviceData.getDiCount3() == null) {
                deviceData.setDiCount3(jsonNode.get("DI_COUNT3").asText());
            }
            
            // 5. 处理pulse_convert_count字段（支持下划线命名）
            if (jsonNode.has("pulse_convert_count") && deviceData.getPulseConvertCount() == null) {
                try {
                    int value = jsonNode.get("pulse_convert_count").asInt();
                    deviceData.setPulseConvertCount((byte) value);
                } catch (Exception e) {
                    logger.warn("解析pulse_convert_count失败: {}", e.getMessage());
                }
            }
            
            // 6. 处理fver字段（JSON中的fver对应数据库中的fw_ver）
            if (jsonNode.has("fver") && (deviceData.getFwVer() == null || deviceData.getFwVer().isEmpty())) {
                try {
                    String fwVer = jsonNode.get("fver").asText();
                    deviceData.setFwVer(fwVer);
                } catch (Exception e) {
                    logger.warn("解析fver失败: {}", e.getMessage());
                }
            }
            
            // 7. 保存原始数据
            deviceData.setRawdata(jsonData);
            
            logger.debug("CQZL协议数据解析成功，IMEI: {}", deviceData.getImei());
            logger.debug("CQZL协议数据解析成功，fwVer: {}", deviceData.getFwVer());
            return deviceData;
            
        } catch (Exception e) {
            logger.error("解析CQZL协议JSON数据失败: {}", e.getMessage(), e);
            return null;
        }
    }


    /**
     * 根据当前接收的数据直接计算新的正累计值（不查询数据库中的当前记录）
     * 
     * 在插入数据之前，根据当前的设备数据和上次的记录计算新的zhengleiji值。并更新表盘数
     * 计算公式：用水量(吨) = ((当前脉冲数 - 上次脉冲数) / 脉冲转换计数) × 单位转换系数
     * 
     * @param currentData 当前接收的设备数据
     * @return 计算后的正累计值字符串，如果计算失败则返回原始值
     */
    private String calculateZhengleijiFromCurrentData(CqzlDeviceReceiveData currentData) {
        try {
            logger.info("开始根据当前数据计算正累计值，IMEI: {}", currentData.getImei());
            
            // 1. 获取脉冲个数配置信息，1个脉冲是一个周期还是2个脉冲是一个周期
            Byte pulseConvertCount = currentData.getPulseConvertCount();
            if (pulseConvertCount == null || pulseConvertCount <= 0) pulseConvertCount = 1; // 默认值

            // 2. 计算当前脉冲数（整数）
            int curDi1 = Integer.parseInt(currentData.getDiCount1().trim());
            int curDi2 = Integer.parseInt(currentData.getDiCount2().trim());
            int curDi3 = Integer.parseInt(currentData.getDiCount3().trim());
            int currentPulseCount = computePulseCount(pulseConvertCount, curDi1, curDi2, curDi3);

            // 3. 根据IMEI查询上次记录的脉冲数和正累计数
            CqzlDeviceReceiveData lastRecord = cqzlDeviceReceiveDataMapper.findByImei(currentData.getImei());
            int lastPulseCount = 0;
            BigDecimal lastZhengleiji = BigDecimal.ZERO;
            
            if (lastRecord != null) {
                int lastDi1 = Integer.parseInt(lastRecord.getDiCount1().trim());
                int lastDi2 = Integer.parseInt(lastRecord.getDiCount2().trim());
                int lastDi3 = Integer.parseInt(lastRecord.getDiCount3().trim());
                lastPulseCount = computePulseCount(pulseConvertCount, lastDi1, lastDi2, lastDi3);
                
                // 获取上次的正累计值
                if (lastRecord.getZhengleiji() != null && !lastRecord.getZhengleiji().isEmpty()) {
                    lastZhengleiji = new BigDecimal(lastRecord.getZhengleiji());
                }
            } else {
                logger.info("未找到上次记录，将使用初始值0，IMEI: {}", currentData.getImei());
            }
            
            // 4. 根据计量单位设置转换系数（直接使用数值：1、0.1、0.01、0.001）
            // unitCoefficient:计量单位
            BigDecimal unitCoefficient = BigDecimal.ONE; // 默认值
            if (currentData.getMeasUnit() != null && !currentData.getMeasUnit().trim().isEmpty()) {
                try {
                    unitCoefficient = new BigDecimal(currentData.getMeasUnit().trim());
                } catch (NumberFormatException e) {
                    logger.warn("无法解析计量单位: {}，使用默认值1", currentData.getMeasUnit());
                    unitCoefficient = BigDecimal.ONE;
                }
            }
            
            // 5. 计算脉冲差值和用水量
            // 本期的脉冲数量
            int pulseDiffInt = currentPulseCount - lastPulseCount;
            if (pulseDiffInt < 0) {
                logger.warn("检测到脉冲差值为负，可能发生设备复位或回绕。IMEI: {}，current: {}，last: {}。按0处理。",
                    currentData.getImei(), currentPulseCount, lastPulseCount);
                pulseDiffInt = 0;
            }
            // 用本期的脉冲数量加上表盘数，在更新表盘数
            int lastDialNumber = 0;
            if (currentData.getDialNumber() != null && !currentData.getDialNumber().trim().isEmpty()) {
                try {
                    lastDialNumber = Integer.parseInt(currentData.getDialNumber().trim());
                } catch (NumberFormatException e) {
                    logger.warn("解析表盘数失败，IMEI: {}，dialNumber: {}，使用默认值0", currentData.getImei(), currentData.getDialNumber());
                    lastDialNumber = 0;
                }
            }
            int currentDialNumber = pulseDiffInt + lastDialNumber;
            dialNumber = String.valueOf(currentDialNumber);


            // 用本期的脉冲数量*单位（unitCoefficient）得出的数，加到上次的正累计数上
            BigDecimal pulseDifference = new BigDecimal(pulseDiffInt);
            BigDecimal waterUsage = pulseDifference
                .divide(new BigDecimal(pulseConvertCount), 3, RoundingMode.HALF_UP)
                .multiply(unitCoefficient)
                .setScale(3, RoundingMode.HALF_UP);
            
            // 6. 计算新的正累计值
            BigDecimal newZhengleiji = lastZhengleiji.add(waterUsage);
            String zhengleijiStr = String.format("%.3f", newZhengleiji);
            
            logger.info("计算完成 - IMEI: {}, 当前脉冲数: {}, 上次脉冲数: {}, 用水量: {} 吨, 新的正累计值: {}", 
                currentData.getImei(), currentPulseCount, lastPulseCount, waterUsage, newZhengleiji);
            
            return zhengleijiStr;
            
        } catch (Exception e) {
            logger.error("计算正累计值失败，IMEI: {}, 错误: {}", currentData.getImei(), e.getMessage(), e);
            // 返回原始值作为后备
            return currentData.getZhengleiji();
        }
    }

    /** 计算脉冲数（整数），根据配置选择 di1 或 (di2+di3)/2，默认返回 di1 */
    private int computePulseCount(Byte pulseConvertCount, int di1, int di2, int di3) {
        if (pulseConvertCount == 1) {
            return di1;
        } else if (pulseConvertCount == 2) {
            return (di2 + di3) / 2;
        } else {
            return di1;
        }
    }
}
